( function () {
/**
 * Octahedron and Quantization encodings based on work by:
 *
 * @link https://github.com/tsherif/mesh-quantization-example
 *
 */

class GeometryCompressionUtils {
  /**
  	 * Make the input mesh.geometry's normal attribute encoded and compressed by 3 different methods.
  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the normal data.
  	 *
  	 * @param {THREE.Mesh} mesh
  	 * @param {String} encodeMethod		"DEFAULT" || "OCT1Byte" || "OCT2Byte" || "ANGLES"
  	 *
  	 */
  static compressNormals(mesh, encodeMethod) {
    if (!mesh.geometry) {
      console.error('Mesh must contain geometry. ');
    }

    const normal = mesh.geometry.attributes.normal;

    if (!normal) {
      console.error('Geometry must contain normal attribute. ');
    }

    if (normal.isPacked) return;

    if (normal.itemSize != 3) {
      console.error('normal.itemSize is not 3, which cannot be encoded. ');
    }

    const array = normal.array;
    const count = normal.count;
    let result;

    if (encodeMethod == 'DEFAULT') {
      // TODO: Add 1 byte to the result, making the encoded length to be 4 bytes.
      result = new Uint8Array(count * 3);

      for (let idx = 0; idx < array.length; idx += 3) {
        const encoded = EncodingFuncs.defaultEncode(array[idx], array[idx + 1], array[idx + 2], 1);
        result[idx + 0] = encoded[0];
        result[idx + 1] = encoded[1];
        result[idx + 2] = encoded[2];
      }

      mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 3, true));
      mesh.geometry.attributes.normal.bytes = result.length * 1;
    } else if (encodeMethod == 'OCT1Byte') {
      /**
      * It is not recommended to use 1-byte octahedron normals encoding unless you want to extremely reduce the memory usage
      * As it makes vertex data not aligned to a 4 byte boundary which may harm some WebGL implementations and sometimes the normal distortion is visible
      * Please refer to @zeux 's comments in https://github.com/mrdoob/three.js/pull/18208
      */
      result = new Int8Array(count * 2);

      for (let idx = 0; idx < array.length; idx += 3) {
        const encoded = EncodingFuncs.octEncodeBest(array[idx], array[idx + 1], array[idx + 2], 1);
        result[idx / 3 * 2 + 0] = encoded[0];
        result[idx / 3 * 2 + 1] = encoded[1];
      }

      mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
      mesh.geometry.attributes.normal.bytes = result.length * 1;
    } else if (encodeMethod == 'OCT2Byte') {
      result = new Int16Array(count * 2);

      for (let idx = 0; idx < array.length; idx += 3) {
        const encoded = EncodingFuncs.octEncodeBest(array[idx], array[idx + 1], array[idx + 2], 2);
        result[idx / 3 * 2 + 0] = encoded[0];
        result[idx / 3 * 2 + 1] = encoded[1];
      }

      mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
      mesh.geometry.attributes.normal.bytes = result.length * 2;
    } else if (encodeMethod == 'ANGLES') {
      result = new Uint16Array(count * 2);

      for (let idx = 0; idx < array.length; idx += 3) {
        const encoded = EncodingFuncs.anglesEncode(array[idx], array[idx + 1], array[idx + 2]);
        result[idx / 3 * 2 + 0] = encoded[0];
        result[idx / 3 * 2 + 1] = encoded[1];
      }

      mesh.geometry.setAttribute('normal', new THREE.BufferAttribute(result, 2, true));
      mesh.geometry.attributes.normal.bytes = result.length * 2;
    } else {
      console.error('Unrecognized encoding method, should be `DEFAULT` or `ANGLES` or `OCT`. ');
    }

    mesh.geometry.attributes.normal.needsUpdate = true;
    mesh.geometry.attributes.normal.isPacked = true;
    mesh.geometry.attributes.normal.packingMethod = encodeMethod; // modify material

    if (!(mesh.material instanceof PackedPhongMaterial)) {
      mesh.material = new PackedPhongMaterial().copy(mesh.material);
    }

    if (encodeMethod == 'ANGLES') {
      mesh.material.defines.USE_PACKED_NORMAL = 0;
    }

    if (encodeMethod == 'OCT1Byte') {
      mesh.material.defines.USE_PACKED_NORMAL = 1;
    }

    if (encodeMethod == 'OCT2Byte') {
      mesh.material.defines.USE_PACKED_NORMAL = 1;
    }

    if (encodeMethod == 'DEFAULT') {
      mesh.material.defines.USE_PACKED_NORMAL = 2;
    }
  }
  /**
  	 * Make the input mesh.geometry's position attribute encoded and compressed.
  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the position data.
  	 *
  	 * @param {THREE.Mesh} mesh
  	 *
  	 */


  static compressPositions(mesh) {
    if (!mesh.geometry) {
      console.error('Mesh must contain geometry. ');
    }

    const position = mesh.geometry.attributes.position;

    if (!position) {
      console.error('Geometry must contain position attribute. ');
    }

    if (position.isPacked) return;

    if (position.itemSize != 3) {
      console.error('position.itemSize is not 3, which cannot be packed. ');
    }

    const array = position.array;
    const encodingBytes = 2;
    const result = EncodingFuncs.quantizedEncode(array, encodingBytes);
    const quantized = result.quantized;
    const decodeMat = result.decodeMat; // IMPORTANT: calculate original geometry bounding info first, before updating packed positions

    if (mesh.geometry.boundingBox == null) mesh.geometry.computeBoundingBox();
    if (mesh.geometry.boundingSphere == null) mesh.geometry.computeBoundingSphere();
    mesh.geometry.setAttribute('position', new THREE.BufferAttribute(quantized, 3));
    mesh.geometry.attributes.position.isPacked = true;
    mesh.geometry.attributes.position.needsUpdate = true;
    mesh.geometry.attributes.position.bytes = quantized.length * encodingBytes; // modify material

    if (!(mesh.material instanceof PackedPhongMaterial)) {
      mesh.material = new PackedPhongMaterial().copy(mesh.material);
    }

    mesh.material.defines.USE_PACKED_POSITION = 0;
    mesh.material.uniforms.quantizeMatPos.value = decodeMat;
    mesh.material.uniforms.quantizeMatPos.needsUpdate = true;
  }
  /**
  	 * Make the input mesh.geometry's uv attribute encoded and compressed.
  	 * Also will change the mesh.material to `PackedPhongMaterial` which let the vertex shader program decode the uv data.
  	 *
  	 * @param {THREE.Mesh} mesh
  	 *
  	 */


  static compressUvs(mesh) {
    if (!mesh.geometry) {
      console.error('Mesh must contain geometry property. ');
    }

    const uvs = mesh.geometry.attributes.uv;

    if (!uvs) {
      console.error('Geometry must contain uv attribute. ');
    }

    if (uvs.isPacked) return;
    const range = {
      min: Infinity,
      max: -Infinity
    };
    const array = uvs.array;

    for (let i = 0; i < array.length; i++) {
      range.min = Math.min(range.min, array[i]);
      range.max = Math.max(range.max, array[i]);
    }

    let result;

    if (range.min >= -1.0 && range.max <= 1.0) {
      // use default encoding method
      result = new Uint16Array(array.length);

      for (let i = 0; i < array.length; i += 2) {
        const encoded = EncodingFuncs.defaultEncode(array[i], array[i + 1], 0, 2);
        result[i] = encoded[0];
        result[i + 1] = encoded[1];
      }

      mesh.geometry.setAttribute('uv', new THREE.BufferAttribute(result, 2, true));
      mesh.geometry.attributes.uv.isPacked = true;
      mesh.geometry.attributes.uv.needsUpdate = true;
      mesh.geometry.attributes.uv.bytes = result.length * 2;

      if (!(mesh.material instanceof PackedPhongMaterial)) {
        mesh.material = new PackedPhongMaterial().copy(mesh.material);
      }

      mesh.material.defines.USE_PACKED_UV = 0;
    } else {
      // use quantized encoding method
      result = EncodingFuncs.quantizedEncodeUV(array, 2);
      mesh.geometry.setAttribute('uv', new THREE.BufferAttribute(result.quantized, 2));
      mesh.geometry.attributes.uv.isPacked = true;
      mesh.geometry.attributes.uv.needsUpdate = true;
      mesh.geometry.attributes.uv.bytes = result.quantized.length * 2;

      if (!(mesh.material instanceof PackedPhongMaterial)) {
        mesh.material = new PackedPhongMaterial().copy(mesh.material);
      }

      mesh.material.defines.USE_PACKED_UV = 1;
      mesh.material.uniforms.quantizeMatUV.value = result.decodeMat;
      mesh.material.uniforms.quantizeMatUV.needsUpdate = true;
    }
  }

}

class EncodingFuncs {
  static defaultEncode(x, y, z, bytes) {
    if (bytes == 1) {
      const tmpx = Math.round((x + 1) * 0.5 * 255);
      const tmpy = Math.round((y + 1) * 0.5 * 255);
      const tmpz = Math.round((z + 1) * 0.5 * 255);
      return new Uint8Array([tmpx, tmpy, tmpz]);
    } else if (bytes == 2) {
      const tmpx = Math.round((x + 1) * 0.5 * 65535);
      const tmpy = Math.round((y + 1) * 0.5 * 65535);
      const tmpz = Math.round((z + 1) * 0.5 * 65535);
      return new Uint16Array([tmpx, tmpy, tmpz]);
    } else {
      console.error('number of bytes must be 1 or 2');
    }
  }

  static defaultDecode(array, bytes) {
    if (bytes == 1) {
      return [array[0] / 255 * 2.0 - 1.0, array[1] / 255 * 2.0 - 1.0, array[2] / 255 * 2.0 - 1.0];
    } else if (bytes == 2) {
      return [array[0] / 65535 * 2.0 - 1.0, array[1] / 65535 * 2.0 - 1.0, array[2] / 65535 * 2.0 - 1.0];
    } else {
      console.error('number of bytes must be 1 or 2');
    }
  } // for `Angles` encoding


  static anglesEncode(x, y, z) {
    const normal0 = parseInt(0.5 * (1.0 + Math.atan2(y, x) / Math.PI) * 65535);
    const normal1 = parseInt(0.5 * (1.0 + z) * 65535);
    return new Uint16Array([normal0, normal1]);
  } // for `Octahedron` encoding


  static octEncodeBest(x, y, z, bytes) {
    let oct, dec, best, currentCos, bestCos; // Test various combinations of ceil and floor
    // to minimize rounding errors

    best = oct = octEncodeVec3(x, y, z, 'floor', 'floor');
    dec = octDecodeVec2(oct);
    bestCos = dot(x, y, z, dec);
    oct = octEncodeVec3(x, y, z, 'ceil', 'floor');
    dec = octDecodeVec2(oct);
    currentCos = dot(x, y, z, dec);

    if (currentCos > bestCos) {
      best = oct;
      bestCos = currentCos;
    }

    oct = octEncodeVec3(x, y, z, 'floor', 'ceil');
    dec = octDecodeVec2(oct);
    currentCos = dot(x, y, z, dec);

    if (currentCos > bestCos) {
      best = oct;
      bestCos = currentCos;
    }

    oct = octEncodeVec3(x, y, z, 'ceil', 'ceil');
    dec = octDecodeVec2(oct);
    currentCos = dot(x, y, z, dec);

    if (currentCos > bestCos) {
      best = oct;
    }

    return best;

    function octEncodeVec3(x0, y0, z0, xfunc, yfunc) {
      let x = x0 / (Math.abs(x0) + Math.abs(y0) + Math.abs(z0));
      let y = y0 / (Math.abs(x0) + Math.abs(y0) + Math.abs(z0));

      if (z < 0) {
        const tempx = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
        const tempy = (1 - Math.abs(x)) * (y >= 0 ? 1 : -1);
        x = tempx;
        y = tempy;
        let diff = 1 - Math.abs(x) - Math.abs(y);

        if (diff > 0) {
          diff += 0.001;
          x += x > 0 ? diff / 2 : -diff / 2;
          y += y > 0 ? diff / 2 : -diff / 2;
        }
      }

      if (bytes == 1) {
        return new Int8Array([Math[xfunc](x * 127.5 + (x < 0 ? 1 : 0)), Math[yfunc](y * 127.5 + (y < 0 ? 1 : 0))]);
      }

      if (bytes == 2) {
        return new Int16Array([Math[xfunc](x * 32767.5 + (x < 0 ? 1 : 0)), Math[yfunc](y * 32767.5 + (y < 0 ? 1 : 0))]);
      }
    }

    function octDecodeVec2(oct) {
      let x = oct[0];
      let y = oct[1];

      if (bytes == 1) {
        x /= x < 0 ? 127 : 128;
        y /= y < 0 ? 127 : 128;
      } else if (bytes == 2) {
        x /= x < 0 ? 32767 : 32768;
        y /= y < 0 ? 32767 : 32768;
      }

      const z = 1 - Math.abs(x) - Math.abs(y);

      if (z < 0) {
        const tmpx = x;
        x = (1 - Math.abs(y)) * (x >= 0 ? 1 : -1);
        y = (1 - Math.abs(tmpx)) * (y >= 0 ? 1 : -1);
      }

      const length = Math.sqrt(x * x + y * y + z * z);
      return [x / length, y / length, z / length];
    }

    function dot(x, y, z, vec3) {
      return x * vec3[0] + y * vec3[1] + z * vec3[2];
    }
  }

  static quantizedEncode(array, bytes) {
    let quantized, segments;

    if (bytes == 1) {
      quantized = new Uint8Array(array.length);
      segments = 255;
    } else if (bytes == 2) {
      quantized = new Uint16Array(array.length);
      segments = 65535;
    } else {
      console.error('number of bytes error! ');
    }

    const decodeMat = new THREE.Matrix4();
    const min = new Float32Array(3);
    const max = new Float32Array(3);
    min[0] = min[1] = min[2] = Number.MAX_VALUE;
    max[0] = max[1] = max[2] = -Number.MAX_VALUE;

    for (let i = 0; i < array.length; i += 3) {
      min[0] = Math.min(min[0], array[i + 0]);
      min[1] = Math.min(min[1], array[i + 1]);
      min[2] = Math.min(min[2], array[i + 2]);
      max[0] = Math.max(max[0], array[i + 0]);
      max[1] = Math.max(max[1], array[i + 1]);
      max[2] = Math.max(max[2], array[i + 2]);
    }

    decodeMat.scale(new THREE.Vector3((max[0] - min[0]) / segments, (max[1] - min[1]) / segments, (max[2] - min[2]) / segments));
    decodeMat.elements[12] = min[0];
    decodeMat.elements[13] = min[1];
    decodeMat.elements[14] = min[2];
    decodeMat.transpose();
    const multiplier = new Float32Array([max[0] !== min[0] ? segments / (max[0] - min[0]) : 0, max[1] !== min[1] ? segments / (max[1] - min[1]) : 0, max[2] !== min[2] ? segments / (max[2] - min[2]) : 0]);

    for (let i = 0; i < array.length; i += 3) {
      quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
      quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
      quantized[i + 2] = Math.floor((array[i + 2] - min[2]) * multiplier[2]);
    }

    return {
      quantized: quantized,
      decodeMat: decodeMat
    };
  }

  static quantizedEncodeUV(array, bytes) {
    let quantized, segments;

    if (bytes == 1) {
      quantized = new Uint8Array(array.length);
      segments = 255;
    } else if (bytes == 2) {
      quantized = new Uint16Array(array.length);
      segments = 65535;
    } else {
      console.error('number of bytes error! ');
    }

    const decodeMat = new THREE.Matrix3();
    const min = new Float32Array(2);
    const max = new Float32Array(2);
    min[0] = min[1] = Number.MAX_VALUE;
    max[0] = max[1] = -Number.MAX_VALUE;

    for (let i = 0; i < array.length; i += 2) {
      min[0] = Math.min(min[0], array[i + 0]);
      min[1] = Math.min(min[1], array[i + 1]);
      max[0] = Math.max(max[0], array[i + 0]);
      max[1] = Math.max(max[1], array[i + 1]);
    }

    decodeMat.scale((max[0] - min[0]) / segments, (max[1] - min[1]) / segments);
    decodeMat.elements[6] = min[0];
    decodeMat.elements[7] = min[1];
    decodeMat.transpose();
    const multiplier = new Float32Array([max[0] !== min[0] ? segments / (max[0] - min[0]) : 0, max[1] !== min[1] ? segments / (max[1] - min[1]) : 0]);

    for (let i = 0; i < array.length; i += 2) {
      quantized[i + 0] = Math.floor((array[i + 0] - min[0]) * multiplier[0]);
      quantized[i + 1] = Math.floor((array[i + 1] - min[1]) * multiplier[1]);
    }

    return {
      quantized: quantized,
      decodeMat: decodeMat
    };
  }

}
/**
 * `PackedPhongMaterial` inherited from THREE.MeshPhongMaterial
 *
 * @param {Object} parameters
 */


class PackedPhongMaterial extends THREE.MeshPhongMaterial {
  constructor(parameters) {
    super();
    this.defines = {};
    this.type = 'PackedPhongMaterial';
    this.uniforms = THREE.UniformsUtils.merge([THREE.ShaderLib.phong.uniforms, {
      quantizeMatPos: {
        value: null
      },
      quantizeMatUV: {
        value: null
      }
    }]);
    this.vertexShader = ['#define PHONG', 'varying vec3 vViewPosition;', '#ifndef FLAT_SHADED', 'varying vec3 vNormal;', '#endif', THREE.ShaderChunk.common, THREE.ShaderChunk.uv_pars_vertex, THREE.ShaderChunk.uv2_pars_vertex, THREE.ShaderChunk.displacementmap_pars_vertex, THREE.ShaderChunk.envmap_pars_vertex, THREE.ShaderChunk.color_pars_vertex, THREE.ShaderChunk.fog_pars_vertex, THREE.ShaderChunk.morphtarget_pars_vertex, THREE.ShaderChunk.skinning_pars_vertex, THREE.ShaderChunk.shadowmap_pars_vertex, THREE.ShaderChunk.logdepthbuf_pars_vertex, THREE.ShaderChunk.clipping_planes_pars_vertex, `#ifdef USE_PACKED_NORMAL
					#if USE_PACKED_NORMAL == 0
						vec3 decodeNormal(vec3 packedNormal)
						{
							float x = packedNormal.x * 2.0 - 1.0;
							float y = packedNormal.y * 2.0 - 1.0;
							vec2 scth = vec2(sin(x * PI), cos(x * PI));
							vec2 scphi = vec2(sqrt(1.0 - y * y), y);
							return normalize( vec3(scth.y * scphi.x, scth.x * scphi.x, scphi.y) );
						}
					#endif

					#if USE_PACKED_NORMAL == 1
						vec3 decodeNormal(vec3 packedNormal)
						{
							vec3 v = vec3(packedNormal.xy, 1.0 - abs(packedNormal.x) - abs(packedNormal.y));
							if (v.z < 0.0)
							{
								v.xy = (1.0 - abs(v.yx)) * vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
							}
							return normalize(v);
						}
					#endif

					#if USE_PACKED_NORMAL == 2
						vec3 decodeNormal(vec3 packedNormal)
						{
							vec3 v = (packedNormal * 2.0) - 1.0;
							return normalize(v);
						}
					#endif
				#endif`, `#ifdef USE_PACKED_POSITION
					#if USE_PACKED_POSITION == 0
						uniform mat4 quantizeMatPos;
					#endif
				#endif`, `#ifdef USE_PACKED_UV
					#if USE_PACKED_UV == 1
						uniform mat3 quantizeMatUV;
					#endif
				#endif`, `#ifdef USE_PACKED_UV
					#if USE_PACKED_UV == 0
						vec2 decodeUV(vec2 packedUV)
						{
							vec2 uv = (packedUV * 2.0) - 1.0;
							return uv;
						}
					#endif

					#if USE_PACKED_UV == 1
						vec2 decodeUV(vec2 packedUV)
						{
							vec2 uv = ( vec3(packedUV, 1.0) * quantizeMatUV ).xy;
							return uv;
						}
					#endif
				#endif`, 'void main() {', THREE.ShaderChunk.uv_vertex, `#ifdef USE_UV
					#ifdef USE_PACKED_UV
						vUv = decodeUV(vUv);
					#endif
				#endif`, THREE.ShaderChunk.uv2_vertex, THREE.ShaderChunk.color_vertex, THREE.ShaderChunk.beginnormal_vertex, `#ifdef USE_PACKED_NORMAL
					objectNormal = decodeNormal(objectNormal);
				#endif

				#ifdef USE_TANGENT
					vec3 objectTangent = vec3( tangent.xyz );
				#endif
				`, THREE.ShaderChunk.morphnormal_vertex, THREE.ShaderChunk.skinbase_vertex, THREE.ShaderChunk.skinnormal_vertex, THREE.ShaderChunk.defaultnormal_vertex, '#ifndef FLAT_SHADED', '	vNormal = normalize( transformedNormal );', '#endif', THREE.ShaderChunk.begin_vertex, `#ifdef USE_PACKED_POSITION
					#if USE_PACKED_POSITION == 0
						transformed = ( vec4(transformed, 1.0) * quantizeMatPos ).xyz;
					#endif
				#endif`, THREE.ShaderChunk.morphtarget_vertex, THREE.ShaderChunk.skinning_vertex, THREE.ShaderChunk.displacementmap_vertex, THREE.ShaderChunk.project_vertex, THREE.ShaderChunk.logdepthbuf_vertex, THREE.ShaderChunk.clipping_planes_vertex, 'vViewPosition = - mvPosition.xyz;', THREE.ShaderChunk.worldpos_vertex, THREE.ShaderChunk.envmap_vertex, THREE.ShaderChunk.shadowmap_vertex, THREE.ShaderChunk.fog_vertex, '}'].join('\n'); // Use the original THREE.MeshPhongMaterial's fragmentShader.

    this.fragmentShader = ['#define PHONG', 'uniform vec3 diffuse;', 'uniform vec3 emissive;', 'uniform vec3 specular;', 'uniform float shininess;', 'uniform float opacity;', THREE.ShaderChunk.common, THREE.ShaderChunk.packing, THREE.ShaderChunk.dithering_pars_fragment, THREE.ShaderChunk.color_pars_fragment, THREE.ShaderChunk.uv_pars_fragment, THREE.ShaderChunk.uv2_pars_fragment, THREE.ShaderChunk.map_pars_fragment, THREE.ShaderChunk.alphamap_pars_fragment, THREE.ShaderChunk.aomap_pars_fragment, THREE.ShaderChunk.lightmap_pars_fragment, THREE.ShaderChunk.emissivemap_pars_fragment, THREE.ShaderChunk.envmap_common_pars_fragment, THREE.ShaderChunk.envmap_pars_fragment, THREE.ShaderChunk.cube_uv_reflection_fragment, THREE.ShaderChunk.fog_pars_fragment, THREE.ShaderChunk.bsdfs, THREE.ShaderChunk.lights_pars_begin, THREE.ShaderChunk.lights_phong_pars_fragment, THREE.ShaderChunk.shadowmap_pars_fragment, THREE.ShaderChunk.bumpmap_pars_fragment, THREE.ShaderChunk.normalmap_pars_fragment, THREE.ShaderChunk.specularmap_pars_fragment, THREE.ShaderChunk.logdepthbuf_pars_fragment, THREE.ShaderChunk.clipping_planes_pars_fragment, 'void main() {', THREE.ShaderChunk.clipping_planes_fragment, 'vec4 diffuseColor = vec4( diffuse, opacity );', 'ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );', 'vec3 totalEmissiveRadiance = emissive;', THREE.ShaderChunk.logdepthbuf_fragment, THREE.ShaderChunk.map_fragment, THREE.ShaderChunk.color_fragment, THREE.ShaderChunk.alphamap_fragment, THREE.ShaderChunk.alphatest_fragment, THREE.ShaderChunk.specularmap_fragment, THREE.ShaderChunk.normal_fragment_begin, THREE.ShaderChunk.normal_fragment_maps, THREE.ShaderChunk.emissivemap_fragment, // accumulation
    THREE.ShaderChunk.lights_phong_fragment, THREE.ShaderChunk.lights_fragment_begin, THREE.ShaderChunk.lights_fragment_maps, THREE.ShaderChunk.lights_fragment_end, // modulation
    THREE.ShaderChunk.aomap_fragment, 'vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;', THREE.ShaderChunk.envmap_fragment, 'gl_FragColor = vec4( outgoingLight, diffuseColor.a );', THREE.ShaderChunk.tonemapping_fragment, THREE.ShaderChunk.encodings_fragment, THREE.ShaderChunk.fog_fragment, THREE.ShaderChunk.premultiplied_alpha_fragment, THREE.ShaderChunk.dithering_fragment, '}'].join('\n');
    this.setValues(parameters);
  }

}

THREE.GeometryCompressionUtils = GeometryCompressionUtils;
THREE.PackedPhongMaterial = PackedPhongMaterial;
} )();
